using System;
using System.Collections.Generic;
using System.ComponentModel;

using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CodePlex.SpaceBlock;
using CodePlex.SpaceBlock.FS;
using System.Threading;
using CodePlex.Resourceful;
using CodePlex.Resourceful.Amazon.S3;
using System.Net;
using CodePlex.SpaceBlock.JobModel;
using CodePlex.SpaceBlock.UI.Controls.Transfer;

namespace CodePlex.SpaceBlock.UI.Controls.Explore
{
    public partial class TransferForm : CodePlex.SpaceBlock.UI.Controls.BaseForm
    {
        private static class Actions
        {
            public static readonly string ClearSelection = "ClearSelection";
            public static readonly string CannedACL = "CannedACL";
            public static readonly string ContentType = "ContentType";
            public static readonly string TransferMetadata = "TransferMetadata";
        }

        private class SystemAndUserMetadata
        {
            public NameValuePairs SystemMetadata = new NameValuePairs();
            public NameValuePairs UserMetadata = new NameValuePairs();

            public override string ToString()
            {
                bool comma = SystemMetadata.Count > 0 && UserMetadata.Count > 0;
                return SystemMetadata.ToString() + (comma?",":"") + UserMetadata.ToString();
            }
        }

        public event EventHandler TransferCompleted;

        public TransferForm()
        {
            InitializeComponent();
        }

        public void Initialize(IFileSystem from, List<Path> fromPaths, IFileSystem to, Path toFolder)
        {


           

            this.Icon = UIManager.Instance.GetIcon(UIConstants.ImageHints.Transfer);


            this.Status("Initializing transfer...");
            this.Title("Initializing transfer...");

            this.Speed("");
            this.Amount("");


            Thread transferThread = null;

            _cancelButton.Click += delegate
            {
                if (transferThread != null)
                {
                    transferThread.Abort();
                }
                this.Dispose();
            };


            _transferButton.Enabled = false;


             List<IFileItem> fromFiles = null;
             List<Path> fromFolders = null;


             _fromBox.Text = from.Caption;
             _toBox.Text = toFolder.ToString();

            DataGridView2 grid = new DataGridView2();
            bool toIsS3 = (to is S3FileSystem);
            bool toIsAzure = (to is AzureBlobStorageFileSystem);
            ISupportsTransferWithMetadata toSupportsMetadata = to as ISupportsTransferWithMetadata;


            int contentTypeIndex = 2;
            int aclIndex = 3;
            int metadataIndex = toSupportsMetadata!=null?(toIsS3 ? 4:3) : 2;
            
            

            ActionManager actions = new ActionManager(this, null, _contextMenu, null);

            // set up context menu
            if (toSupportsMetadata != null)
            {
                
                grid.ContextMenuStrip = _contextMenu;



                actions.NewAction(Actions.ContentType, "Set Content-Type", 0, Keys.None, delegate
                   {

                   });

                foreach (string contentTypeCategory in SpaceBlockUtils.GetContentTypeCategories())
                {

                    string categoryName = Actions.ContentType + "." + contentTypeCategory;

                    actions.NewSubAction(Actions.ContentType, categoryName, contentTypeCategory, 0, Keys.None, delegate
                    {

                    });

                    foreach (string contentType in SpaceBlockUtils.GetContentTypes(contentTypeCategory))
                    {
                        string localContentType = contentType;
                        string name = categoryName + "." + contentType;

                        actions.NewSubAction(categoryName, name, contentType, 0, Keys.None, delegate
                        {

                            actions.SetChecked(Actions.ContentType, false);
                            actions.SetChecked(name, true);

                            foreach (DataGridViewRow row in grid.SelectedRows)
                            {
                                if (row.Tag is IFileItem)
                                {
                                    row.Cells[contentTypeIndex].Value = localContentType;
                                }
                            }

                        });
                    }


                }



                actions.NewSubSeparator(Actions.ContentType, "ContentTypeSeparator");
                actions.NewSubAction(Actions.ContentType, "CustomContentType", "Custom...", 0, null, delegate
                {
                      InputboxForm customForm = new InputboxForm();

                      customForm.Icon = this.Icon;

                      customForm.Title = Properties.Messages.CustomContentType_Title;
                      customForm.Prompt = "Content-Type:";
                      customForm.ValueHorizonalOffset += 20;
                      customForm.Description = Properties.Messages.CustomContentType_Description;

                      if (customForm.ShowDialog(this) == DialogResult.OK)
                       {
                           string value = customForm.Value.Trim();

                           if (value.Length == 0)
                           {
                               return;
                           }

                           // TODO copied from above - horrific...
                           foreach (DataGridViewRow row in grid.SelectedRows)
                           {
                               if (row.Tag is IFileItem)
                               {
                                   row.Cells[contentTypeIndex].Value = value;
                               }
                           }

                       }
                });


                if (toIsS3)
                {
            
                    actions.NewAction(Actions.CannedACL, "Set Canned ACL", 0, Keys.None, delegate
                    {

                    });

                    foreach (string cannedACL in S3Constants.CannedAccessPolicies.All)
                    {

                        string localCannedACL = cannedACL;
                        string name = Actions.CannedACL + "." + cannedACL;

                        actions.NewSubAction(Actions.CannedACL, name, cannedACL, 0, Keys.None, delegate
                        {
                            actions.SetChecked(Actions.CannedACL, false);
                            actions.SetChecked(name, true);

                            foreach (DataGridViewRow row in grid.SelectedRows)
                            {
                                if (row.Tag is IFileItem)
                                {
                                    row.Cells[aclIndex].Value = localCannedACL;
                                }
                            }
                        });



                    }

                }

                TransferMetadataForm form = new TransferMetadataForm();
                form.Icon = this.Icon;

                actions.NewAction(Actions.TransferMetadata, "Edit Metadata...", 0, Keys.None, delegate
                {




                    SystemAndUserMetadata firstSelected = grid.SelectedRows[0].Cells[metadataIndex].Value as SystemAndUserMetadata;



                    string[] systemHeaderNames = new string[0];

                    if (toIsS3)
                    {
                        systemHeaderNames = new string[] 
                        { 
                            // these are explicitly specified in the S3 docs 
                            HttpHeader.CacheControl,
                            S3Constants.Headers.ContentDisposition,
                            HttpHeader.ContentEncoding,
                            HttpHeader.Expires,

                        };
                    }
                    if (toIsAzure)
                    {
                        systemHeaderNames = new string[] 
                        { 
                            // these are the only ones ms lets through
                            HttpHeader.ContentLanguage,
                            HttpHeader.ContentEncoding,
       

                        };
                    }



                    form.Initialize(systemHeaderNames, firstSelected.SystemMetadata, (toIsS3 ? UnencodeS3(firstSelected.UserMetadata) : firstSelected.UserMetadata));

                    if (form.ShowDialog(this) == DialogResult.OK)
                    {

                        SystemAndUserMetadata m = new SystemAndUserMetadata();
                        m.SystemMetadata = form.SystemMetadata;
                        m.UserMetadata = toIsS3 ? EncodeS3(form.UserMetadata) : form.UserMetadata;
                        


                        foreach (DataGridViewRow row in grid.SelectedRows)
                        {
                            if (row.Tag is IFileItem)
                            {
                                row.Cells[metadataIndex].Value = m;
                            }
                        }
                    }

                });

                actions.NewAction(Actions.ClearSelection, "Clear Selection", 0, Keys.None, delegate
                {
                    grid.ClearSelection();
                });

              

            }

            this.InBackground(delegate
            {

                fromFiles = new List<IFileItem>();
                fromFolders = new List<Path>();

                Action<Path> add = null;
                
                add = delegate(Path folderPath)
                {
                    fromFolders.Add(folderPath);

                    foreach (IPathItem item in from.GetItems(folderPath))
                    {
                        if (item.Path.IsFolder)
                        {
                            add(item.Path);
                        }
                        else
                        {
                            fromFiles.Add(item as IFileItem);
                        }
                    }
                };


                Path basePath = null;
                foreach(Path fromPath in fromPaths) {

                    basePath = basePath ?? fromPath.Parent;

                    if (fromPath.IsFolder)
                    {
                        add(fromPath);
                    }
                    else
                    {
                        fromFiles.Add(from.GetItem(fromPath) as IFileItem);
                    }
                }

                this.InDisplay(delegate
                {
                    
                    DataGridViewColumn col = grid.AddColumnText("", 300);
                    col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

                    if (toSupportsMetadata != null)
                    {
                        grid.AddColumnText("Content Type", 150);
                        if (toIsS3)
                        {
                          
                            grid.AddColumnText("Canned ACL", 150);
                        }
                        grid.AddColumnText("Metadata", 150);
                    }
                   
                    foreach (Path fromFolder in fromFolders)
                    {
                        string to2 = fromFolder.ToString().Substring(basePath.ToString().Length);

                        List<object> values = new List<object>();
                        values.Add(UIManager.Instance.GetSmallImage(UIConstants.ImageHints.FolderClosed));
                        values.Add(to2);
                        if (toSupportsMetadata != null)
                        {
                            if (toIsS3)
                            {
                                values.Add("");
                                values.Add("");
                            }
                            values.Add("");
                        }
                        
                        grid.AddRow(fromFolder, values.ToArray());
                    }


                    string cannedAccessPolicy = S3Constants.CannedAccessPolicies.Private;
                    if (toIsS3)
                    {
                        // determine which canned access policy to display by default (private or public-read)
                        S3DefaultTransferACL defaultAcl = ((S3FileSystem)to).DefaultTransferACL;
                        if (defaultAcl.Equals(S3DefaultTransferACL.PublicRead))
                        {
                            cannedAccessPolicy = S3Constants.CannedAccessPolicies.PublicRead;
                        }
                        else if (defaultAcl.Equals(S3DefaultTransferACL.Bucket))
                        {
                            try
                            {
                                string bucket = toFolder.Parts[0];

                                ACL bucketACL = ((S3FileSystem)to).S3Connection.GetACL(bucket);

                                if (bucketACL.GetPermissions(S3Constants.Principals.AllUsers).Contains(S3Constants.Permissions.Read)
                                    || bucketACL.GetPermissions(S3Constants.Principals.AllUsers).Contains(S3Constants.Permissions.FullControl)
                                    )
                                {
                                    cannedAccessPolicy = S3Constants.CannedAccessPolicies.PublicRead;
                                }
                            }
                            catch { }
                        }

                    }



                    foreach (IFileItem fromFile in fromFiles)
                    {
                        string to2 = fromFile.Path.ToString().Substring(basePath.ToString().Length);

                        List<object> values = new List<object>();
                        values.Add(UIManager.Instance.GetSmallImage(UIConstants.ImageHints.File));
                        values.Add(to2);
                        if (toSupportsMetadata != null)
                        {
                            values.Add(MimeMap.Get(fromFile.Path.ToString()));
                            if (toIsS3)
                            {
                                values.Add(cannedAccessPolicy);
                            }
                            values.Add(new SystemAndUserMetadata());
                        }

                        
                        grid.AddRow(fromFile, values.ToArray());
                    }

                    grid.Sort(grid.Columns[1], ListSortDirection.Ascending);
                    UIUtils.FillWith(_itemsHolder, grid);

                    this.Status("Ready.");
                    
                    this.Title("Transfer ready to {0}.", to.Caption);

                    _transferButton.Enabled = true;

                });


            });



            // new transfer code

            _transferButton.Click += delegate
            {
            
                
                TransferJob job = new TransferJob(to);
                foreach (DataGridViewRow row in grid.Rows)
                {


                    string part = row.Cells[1].Value as string;

                    Path toPath = toFolder.Combine(part);

                    // upload folder
                    Path fromFolder = row.Tag as Path;
                    if (fromFolder != null)
                    {
                        job.AddFolder(from,fromFolder, toPath);
                        continue;
                    }


                    // upload file
                    IFileItem fromFile = row.Tag as IFileItem;
                    if (fromFile == null)
                    {
                        continue;
                    }

                    NameValuePairs metadata = new NameValuePairs();
                    if (toSupportsMetadata != null)
                    {


                        metadata[HttpHeader.ContentType] = row.Cells[contentTypeIndex].Value as string;
                        if (toIsS3)
                        {
                            // x -> s3
                            metadata[S3Constants.Headers.CannedAccessPolicy] = row.Cells[aclIndex].Value as string;
                        }

                        SystemAndUserMetadata meta = row.Cells[metadataIndex].Value as SystemAndUserMetadata;
                        if (meta != null)
                        {
                            metadata.AddRange(meta.SystemMetadata);
                            metadata.AddRange(meta.UserMetadata);
                        }
                        

                    }


                    job.AddFile(fromFile, toPath, metadata);


                }

                Guid jobID = UIManager.Instance.JobManager.Enqueue(job);

                this.Dispose();

                UIManager.Instance.MainForm.ShowView<TransfersView>();
                UIManager.Instance.MainForm.GetView<TransfersView>().ControlStrong.Select(jobID);



            };



        }















        private NameValuePairs EncodeS3(NameValuePairs unencoded)
        {
            NameValuePairs rt = new NameValuePairs();
            foreach (string key in unencoded.Keys)
            {
                string value = unencoded[key];

                rt[S3Constants.Headers.AmazonUserMetadataPrefix + key] = value;
            }
            return rt;
        }

        private NameValuePairs UnencodeS3(NameValuePairs encoded)
        {
            NameValuePairs rt = new NameValuePairs();
            foreach (string key in encoded.Keys)
            {
                string value = encoded[key];

                rt[key.Substring(S3Constants.Headers.AmazonUserMetadataPrefix.Length)] = value;
            }
            return rt;
        }












        private void Title(string message, params object[] parameters)
        {
            this.Text = string.Format(message, parameters);
        }

        private void Status(string message, params object[] parameters)
        {
            _statusBox.Text = string.Format(message, parameters);
        }

        private void Speed(string message, params object[] parameters)
        {
            _speedBox.Text = string.Format(message, parameters);
        }

        private void Amount(string message, params object[] parameters)
        {
            _amountBox.Text = string.Format(message, parameters);
        }

        protected void OnTransferCompleted()
        {
            if (this.TransferCompleted != null)
            {
                this.TransferCompleted(this, EventArgs.Empty);
            }
        }


    }
}

